<?php

namespace Api\Controller;

use Think\Controller;

class SocketController extends CommonController {

    //Swoole类
    protected $server;
    //Redis类
    protected $redis;
    //Swoole Server返回对象
    protected $swooleReturnData;

    //模型类
    protected $orderModel;
    protected $userModel;
    protected $orderItemModel;
    protected $ruleModel;
    protected $ladderModel;
    protected $payModel;
    protected $accountDetailModel;
    protected $currentUser;

    function _initialize() {
        parent::_initialize();
        $this->orderModel = D('order');
        $this->userModel = D('user');
        $this->orderItemModel = D('orderItem');
        $this->ruleModel = D('rule');
        $this->ladderModel = D('ladder');
        $this->payModel = D('pay');
        $this->accountDetailModel = D('accountDetail');
    }

    public function notificationSystem() {

        //创建swooleTable
        $table = new \swoole_table(1024);
        $table->column('ladder_id', \swoole_table::TYPE_INT);
        $table->create();

        //创建websocket服务器对象，监听0.0.0.0:9502端口
        $this->server = new \swoole_websocket_server("0.0.0.0", 9502);
        $this->server->set(array(
            'worker_num' => 4,
            'reactor_num' => 4,
            'max_request' => 0,
            'backlog' => 128,
            'dispatch_mode' => 2,
            'daemonize' => 1,
            'heartbeat_check_interval' => 60,
            'heartbeat_idle_time' => 600,
            'log_file' => __DIR__.'/log/webs_swoole.log',
        ));

        $this->server->table = $table;

        //建立连接
        $this->server->on('Open', function ($serv, $req) {
            print_r("Welcome to Skyyoyo Notification System! Client " .$req->fd. " Connected!\n");
        });

        //接受客户端发送的activityId并与客户端id绑定
        $this->server->on('Message', function ($serv, $frame) {

            //获取用户信息
            $ladderId = $frame->data;

            //将将用户信息保存到SwooleTable
            $this->server->table->set($frame->fd, array('ladder_id' => $ladderId));

            try {
                //在线凭据
                S('ladder_id_'.$ladderId,$frame->fd);

                //修改为空闲状态
                $ladderData['is_busy'] = C('LADDER_IS_BUSY.NOTBUSY');
                $this->ladderModel->where(array('ladder_id' => $ladderId))->save($ladderData);
            } catch (\Exception $e) {
                print_r("Error Code: " .$e->getCode(). ", Error Message: " .$e->getMessage(). "\n");
                return;
            }

        });

        //进行投币操作
        $this->server->on('Request', function ($req, $resp) {

            //header配置
            $resp->header("Access-Control-Allow-Origin","*");
            $resp->header("Access-Control-Allow-Headers","X-Requested-With,SIGN");
            $resp->header("","HTTP/1.1 200 OK");
            $resp->header("Status","200 OK");
            $resp->header("Content-Type","application/json; charset=utf-8");

            try {
                //进行Swoole Server初始化
                if($this->swooleInitialize($req)) {
                    //获取服务类型
                    $serviceType = $this->getPath($req);

                    switch ($serviceType) {

                        case C('SWOOLE_SERVER_SERVICE.TEST'):

                            $ladderId = $this->pushTest($req);
                            //推送
                            if($ladderId) {
                                $fd = $this->checkOnline($ladderId);
                                if($fd) {
                                    $this->server->push($fd,$this->getSocketMessage('TEST_MESSAGE'));
                                }
                            }
                            break;

                        case C('SWOOLE_SERVER_SERVICE.ORDER'):

                            $info = $this->WechatPay($req);

                            if(!isNullOrEmpty($info)) {
                                $fd = $this->checkOnline($info['ladder_id']);
                                if($fd) {
                                    if($info['type'] == C('ORDER_TYPE.FAST_MATCH')) {
                                        $this->server->push($fd,$this->getSocketMessage('FAST_ORDER'));
                                    } else if($info['type'] == C('ORDER_TYPE.ADVANCED_FILER')) {
                                        $this->server->push($fd,$this->getSocketMessage('MORE_ORDER'));
                                    } else {
                                        $this->server->push($fd,$this->getSocketMessage('BIND_LADDER_ORDER'));
                                    }
                                }
                            }
                            break;

                        case C('SWOOLE_SERVER_SERVICE.SUCCESS'):

                            $ladderId = $this->userSuccessGame($req);
                            //推送
                            if($ladderId) {
                                $fd = $this->checkOnline($ladderId);
                                if($fd) {
                                    $this->server->push($fd,$this->getSocketMessage('USER_SUCCESS'));
                                }
                            }
                            break;

                        case C('SWOOLE_SERVER_SERVICE.AGAIN'):

                            $ladderId = $this->userPlayAgain($req);
                            //推送
                            if($ladderId) {
                                $fd = $this->checkOnline($ladderId);
                                if($fd) {
                                    $this->server->push($fd,$this->getSocketMessage('USER_AGAIN'));
                                }
                            }
                            break;

                        case C('SWOOLE_SERVER_SERVICE.END'):

                            $ladderId = $this->userEndGame($req);
                            //推送
                            if($ladderId) {
                                $fd = $this->checkOnline($ladderId);
                                if($fd) {
                                    $this->server->push($fd,$this->getSocketMessage('USER_END'));
                                }
                            }
                            break;

                        case C('SWOOLE_SERVER_SERVICE.REMIND_TWENTY'):

                            $ladderIdList = $this->twentyMinutesRemind();
                            //推送
                            if(!isNullOrEmpty($ladderIdList)) {
                                foreach ($ladderIdList as $ladderId) {
                                    $fd = $this->checkOnline($ladderId);
                                    if($fd) {
                                        $this->server->push($fd,$this->getSocketMessage('SYSTEM_REMIND_TWENTY'));
                                    }
                                }
                            }
                            break;

                        case C('SWOOLE_SERVER_SERVICE.REMIND_EXPIRE'):

                            $ladderIdList = $this->expireLadderConfirms();
                            //推送
                            if(!isNullOrEmpty($ladderIdList)) {
                                foreach ($ladderIdList as $ladderId) {
                                    $fd = $this->checkOnline($ladderId);
                                    if($fd) {
                                        $this->server->push($fd,$this->getSocketMessage('SYSTEM_REMIND_EXPIRE'));
                                    }
                                }
                            }
                            break;

                        case C('SWOOLE_SERVER_SERVICE.REMIND_SUCCESS'):

                            $ladderIdList = $this->expireNewOrders();
                            //推送
                            if(!isNullOrEmpty($ladderIdList)) {
                                foreach ($ladderIdList as $ladderId) {
                                    $fd = $this->checkOnline($ladderId);
                                    if($fd) {
                                        $this->server->push($fd,$this->getSocketMessage('SYSTEM_REMIND_SUCCESS'));
                                    }
                                }
                            }
                            break;

                    }
                }
            } catch (\Exception $e) {
                print_r("Error Code: " .$e->getCode(). ", Error Message: " .$e->getMessage(). "\n");
                return;
            }

            if($serviceType != C('SWOOLE_SERVER_SERVICE.ORDER')) {
                $resp->end(json_encode($this->swooleReturnData));
            } else {
                $resp->end("SUCCESS");
            }
        });

        //关闭连接时触发
        $this->server->on('Close', function ($serv, $fd) {

            try {
                //修改为忙碌状态
                $ladder = $this->server->table->get($fd);
                $ladderData['is_busy'] = C('LADDER_IS_BUSY.BUSY');
                $this->ladderModel->where(array('ladder_id' => $ladder['ladder_id']))->save($ladderData);
            } catch (\Exception $e) {
                print_r("Error Code: " .$e->getCode(). ", Error Message: " .$e->getMessage(). "\n");
                return;
            }

            //删除关闭的连接
            $this->server->table->del($fd);

            print_r("Client " .$fd. " is Disconnected from Skyyoyo Notification System!\n");

        });

        //启动Swoole服务
        $this->server->start();

    }

    //mock
    public function pushTest($req) {
        //向当前用户推送进度
        $ladderId = $req->post['ladderId'];

        if(!isNullOrEmpty($ladderId)) {
            $this->swooleReturnData['data']['ladder_id'] = $ladderId;
            return $ladderId;
        } else {
            $this->swooleErrorReturn('ERROR_LADDER_NOT_FOUND');
            return false;
        }
    }

    //打手在线情况校验
    public function checkOnline($ladderId) {
        $fd = S('ladder_id_'.$ladderId);

        if(isNullOrEmpty($fd)) {
            return false;
        }

        if(!$this->server->table->exist($fd)) {
            return false;
        }

        $table = $this->server->table->get($fd);
        if($ladderId != $table['ladder_id']) {
            return false;
        }

        return $fd;
    }

    //swoole服务器初始化
    public function swooleInitialize($req) {
        //返回参数初始化
        $this->swooleReturnData = array();
        $this->swooleReturnData['errorCode'] = 200;
        $this->swooleReturnData['errorMsg'] = '';

        //校验用户
        $token = $req->post['token'];

        $userMap['token'] = $token;
        $this->currentUser = $this->userModel->where($userMap)->find();

//        if(isNullOrEmpty($this->currentUser)) {
//            $this->swooleErrorReturn('ERROR_TOKEN');
//            return false;
//        }

        return true;
    }

    //swoole服务器错误代码返回
    public function swooleErrorReturn($errorEnum) {
        //返回数据
        $this->swooleReturnData['errorCode'] = C('ERROR_CODE.' . $errorEnum);
        $this->swooleReturnData['errorMsg'] = C('ERROR_MSG.' . $errorEnum);
    }

    //配置返回消息类型
    public function getSocketMessage($message) {

        $message = C('SWOOLE_MESSAGE_DATA.'.$message);

        $messageData['message'] = $message;

        return json_encode($messageData);

    }

    public function getPath($req) {
        $path = !isNullOrEmpty($req->server['path_info']) ? $req->server['path_info'] : !isNullOrEmpty($req->server['request_uri']) ? $req->server['request_uri'] : '/';
        return $path;
    }

    //用户完成支付
    public function WechatPay($req){

        $order=$req->rawContent();

        $array_data = json_decode(json_encode(simplexml_load_string($order, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

        if($array_data['return_code']=='SUCCESS') {

            echo 'SUCCESS';

            //查找付款记录
            $payMap['out_trade_no'] = $array_data['out_trade_no'];
            $pay = M('pay')->where($payMap)->find();

            //修改付款状态
            $payData['status'] = 1;

            M('pay')->where($payMap)->save($payData);

            //修改订单状态
            $orderMap['pay_id'] = $pay['pay_id'];
            $order = M('order')->alias('o')
                ->join('left join __USER__ u ON o.user_id = u.user_id')
                ->join('left join __ORDER_ITEM__ oi ON o.order_id = oi.order_id')
                ->join('left join __LADDER__ l ON oi.ladder_id = l.ladder_id')
                ->join('left join __USER__ uu on l.user_id = uu.user_id')
                ->field('o.*, l.ladder_id, u.mobile as player_mobile, uu.mobile as ladder_mobile')
                ->where($orderMap)
                ->find();

            $returnData['ladder_id'] = $order['ladder_id'];
            $returnData['type'] = $order['type'];

            $orderData['status'] = 1;

            M('order')->where($orderMap)->save($orderData);

            //修改订单项目状态
            $orderItemMap['order_id'] = $order['order_id'];

            $orderItemData['status'] = 1;
            $orderItemData['begin_time'] = getCurrentDate();

            M('orderItem')->where($orderItemMap)->save($orderItemData);

            //发送短信提示匹配成功
            $content = "您的订单已匹配成功，请查看公众账号内订单信息，进入游戏，互加好友，祝游戏顺利！";

            //发送给用户
            mms(C('mmskey'), $order['player_mobile'], $content);
            //发送给打手
            mms(C('mmskey'), $order['ladder_mobile'], $content);

            return $returnData;

        }
    }

    //比赛胜利操作
    public function userSuccessGame($req) {

        $itemId = $req->post['itemId'];

        $orderItem = $this->orderItemModel
            ->field('*, TIMESTAMPDIFF(MINUTE,begin_time,NOW()) as game_last_time')
            ->where(array('item_id' => $itemId))
            ->find();

        $order = $this->orderModel->where(array('order_id' => $orderItem['order_id']))->find();

        //判断订单是否存在
        if (isNullOrEmpty($orderItem) || isNullOrEmpty($order)) {
            $this->swooleErrorReturn('ERROR_ORDER_NOT_FOUND');
            return false;
        }

        //判断订单是否为进行中
        if($order['status'] != C('ORDER_STATUS.NEW') || $orderItem['status'] != C('ORDER_ITEM_STATUS.NEW')) {
            $this->swooleErrorReturn('ERROR_ORDER_STATUS_ERROR');
            return false;
        }

        //判断游戏时间是否达到20分钟
        if($orderItem['game_last_time'] < 20) {
            $this->swooleErrorReturn('ERROR_ORDER_ITEM_LAST_TIME_NOT_MATCH');
            return false;
        }

        //修改订单项目状态
        $orderItemData['status'] = C('ORDER_ITEM_STATUS.SUCCESS');
        $orderItemData['end_time'] = getCurrentDate();
        $this->orderItemModel->where(array('item_id' => $orderItem['item_id']))->save($orderItemData);

        //修改订单状态
        $orderData['status'] = C('ORDER_STATUS.END');
        $orderData['end_time'] = getCurrentDate();
        $this->orderModel->where(array('order_id' => $orderItem['order_id']))->save($orderData);

        //增加流水
        $accountDetailData['ladder_id'] = $orderItem['ladder_id'];
        $accountDetailData['create_time'] = getCurrentDate();
        $accountDetailData['detail_type'] = C('SCORE_DETAIL_TYPE.ORDER');
        $accountDetailData['money'] = getFinalIncome($order['money']);
        $accountDetailData['order_id'] = $orderItem['order_id'];

        $this->accountDetailModel->add($accountDetailData);

        $ladder = $this->ladderModel->where(array('ladder_id' => $orderItem['ladder_id']))->find();

        //修改陪练信息
        $ladderData['gaming_status'] = C('LADDER_GAMING_STATUS.FREE');
        $ladderData['total_assets'] = $ladder['total_assets'] + getFinalIncome($order['money']);
        $ladderData['last_game_end_time'] = getCurrentDate();

        $this->ladderModel->where(array('ladder_id' => $orderItem['ladder_id']))->save($ladderData);

        return $orderItem['ladder_id'];

    }

    //用户请求再来一局操作（失败）
    public function userPlayAgain($req) {

        $itemId = $req->post['itemId'];

        $orderItem = $this->orderItemModel
            ->field('*, TIMESTAMPDIFF(MINUTE,begin_time,NOW()) as game_last_time')
            ->where(array('item_id' => $itemId))
            ->find();

        $order = $this->orderModel->where(array('order_id' => $orderItem['order_id']))->find();

        //判断订单是否存在
        if(isNullOrEmpty($orderItem) || isNullOrEmpty($order)) {
            $this->swooleErrorReturn('ERROR_ORDER_NOT_FOUND');
            return false;
        }

        //判断订单是否为进行中
        if($order['status'] != C('ORDER_STATUS.NEW') || $orderItem['status'] != C('ORDER_ITEM_STATUS.NEW')) {
            $this->swooleErrorReturn('ERROR_ORDER_STATUS_ERROR');
            return false;
        }

        //判断游戏时间是否达到20分钟
        if($orderItem['game_last_time'] < 20) {
            $this->swooleErrorReturn('ERROR_ORDER_ITEM_LAST_TIME_NOT_MATCH');
            return false;
        }

        //修改订单项目状态为失败再来一局待确认
        $orderItemData['status'] = C('ORDER_ITEM_STATUS.FAIL_REMATCH_CONFIRM');
        $orderItemData['fail_request_time'] = getCurrentDate();
        $this->orderItemModel->where(array('item_id' => $orderItem['item_id']))->save($orderItemData);

        return $orderItem['ladder_id'];

    }

    //用户请求结束游戏操作（失败）
    public function userEndGame($req) {

        $itemId = $req->post['itemId'];

        $orderItem = $this->orderItemModel
            ->field('*, TIMESTAMPDIFF(MINUTE,begin_time,NOW()) as game_last_time')
            ->where(array('item_id' => $itemId))
            ->find();

        $order = $this->orderModel->where(array('order_id' => $orderItem['order_id']))->find();

        //判断订单是否存在
        if (isNullOrEmpty($orderItem) || isNullOrEmpty($order)) {
            $this->swooleErrorReturn('ERROR_ORDER_NOT_FOUND');
            return false;
        }

        //判断订单是否为进行中
        if($order['status'] != C('ORDER_STATUS.NEW') || $orderItem['status'] != C('ORDER_ITEM_STATUS.NEW')) {
            $this->swooleErrorReturn('ERROR_ORDER_STATUS_ERROR');
            return false;
        }

        //判断游戏时间是否达到20分钟
        if($orderItem['game_last_time'] < 20) {
            $this->swooleErrorReturn('ERROR_ORDER_ITEM_LAST_TIME_NOT_MATCH');
            return false;
        }

        //修改订单项目状态为失败待确认
        $orderItemData['status'] = C('ORDER_ITEM_STATUS.FAIL_CONFIRM');
        $orderItemData['fail_request_time'] = getCurrentDate();
        $this->orderItemModel->where(array('item_id' => $orderItem['item_id']))->save($orderItemData);

        return $orderItem['ladder_id'];

    }

    //发送短信提示20分钟已到（定时器）
    public function twentyMinutesRemind() {

        $map['o.status'] = C('ORDER_STATUS.NEW');
        $map['oi.status'] = C('ORDER_ITEM_STATUS.NEW');
        $map['remind_message_sent'] = C('REMIND_MESSAGE_STATUS.NO');
        $map['TIMESTAMPDIFF(MINUTE,oi.begin_time,NOW())'] = array('egt',20);

        $orderList = $this->orderModel->alias('o')
            ->join('left join __USER__ u ON o.user_id = u.user_id')
            ->join('left join __ORDER_ITEM__ oi ON o.order_id = oi.order_id')
            ->join('left join __LADDER__ l ON oi.ladder_id = l.ladder_id')
            ->join('left join __USER__ uu on l.user_id = uu.user_id')
            ->field('oi.item_id, u.mobile as player_mobile, uu.mobile as ladder_mobile, l.ladder_id')
            ->where($map)
            ->select();

        if(!isNullOrEmpty($orderList)) {

            $playerContent = "游戏结束时请及时对游戏结果进行正确的反馈，以便我们对订单进行实时处理，谢谢！";
            $ladderContent = "游戏结束时请及时提醒老板对游戏结果进行正确的反馈，以便我们对订单进行实时处理，谢谢！";

            foreach ($orderList as $order) {
                //发送给用户
                mms(C('mmskey'), $order['player_mobile'], $playerContent);
                //发送给打手
                mms(C('mmskey'), $order['ladder_mobile'], $ladderContent);
            }

            $itemIdList = array_unique(array_column($orderList,'item_id'));
            $ladderIdList = array_unique(array_column($orderList,'ladder_id'));

            $updateMap['item_id'] = array('in',$itemIdList);
            $updateData['remind_message_sent'] = C('REMIND_MESSAGE_STATUS.YES');

            $this->orderItemModel->where($updateMap)->save($updateData);

        }

        return $ladderIdList;

    }

    //检查陪练确认时间是否超过2分钟（定时器）
    public function expireLadderConfirms() {

        $map['status'] = array('in','3,6');
        $map['TIMESTAMPDIFF(MINUTE,fail_request_time,NOW())'] = array('egt',2);

        $orderItemList = $this->orderItemModel->where($map)->select();

        if(!isNullOrEmpty($orderItemList)) {
            $ladderIdList = array_unique(array_column($orderItemList,'ladder_id'));
            $itemIdList = getKeyIdList($orderItemList,'item_id');

            $sql = "UPDATE __ORDER_ITEM__ oi
                    LEFT JOIN __ORDER__ o
                    ON oi.order_id = o.order_id
                    LEFT JOIN __LADDER__ l
                    ON oi.ladder_id = l.ladder_id
                    SET oi.status = " .C('ORDER_ITEM_STATUS.FAIL_HOLD'). ", 
                    oi.end_time = '" .getCurrentDate(). "', 
                    o.status = " .C('ORDER_STATUS.HOLD'). ",  
                    l.gaming_status = " .C('LADDER_GAMING_STATUS.FREE'). ", 
                    l.last_game_end_time = '" .getCurrentDate(). "'
                    WHERE oi.item_id IN (" .$itemIdList. ")";

            M()->execute($sql);
        }

        return $ladderIdList;

    }

    //检查进行中订单是否超过两小时
    public function expireNewOrders() {

        $map['status'] = C('ORDER_ITEM_STATUS.NEW');
        $map['TIMESTAMPDIFF(MINUTE,begin_time,NOW())'] = array('egt',120);

        $orderItemList = $this->orderItemModel->where($map)->select();

        if(!isNullOrEmpty($orderItemList)) {
            $ladderIdList = array_unique(array_column($orderItemList,'ladder_id'));
            $itemIdList = getKeyIdList($orderItemList,'item_id');

            $sql = "UPDATE __ORDER_ITEM__ oi
                LEFT JOIN __ORDER__ o
                ON oi.order_id = o.order_id
                LEFT JOIN __LADDER__ l
                ON oi.ladder_id = l.ladder_id
                SET oi.status = " .C('ORDER_ITEM_STATUS.SUCCESS'). ", 
                oi.end_time = '" .getCurrentDate(). "', 
                o.status = " .C('ORDER_STATUS.END'). ", 
                o.end_time = '" .getCurrentDate(). "', 
                l.gaming_status = " .C('LADDER_GAMING_STATUS.FREE'). ", 
                l.total_assets = l.total_assets + o.money, 
                l.last_game_end_time = '" .getCurrentDate(). "'
                WHERE oi.item_id IN (" .$itemIdList. ")";

            M()->execute($sql);
        }

        return $ladderIdList;

    }

}
